/*
 * Copyright 2008 Nathan C Jones
 * 
 * This file is part of Fivehundred.
 * 
 * Fivehundred is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * Fivehundred is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * Fivehundred. If not, see <http://www.gnu.org/licenses/>. 
 */

package com.googlecode.fivehundred.ui;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;

import com.googlecode.fivehundred.model.Card;

/**
 * A graphical representation of a card.
 * 
 * @author Nathan C Jones
 */
public class GraphicalCard extends AbstractGraphicalComponent {

	/** The colour used to draw the card shadow. */
	private static final Color SHADOW_COLOUR = new Color(64, 64, 64, 64);

	private static final Color SELECT_COLOR_1 = new Color(127, 0, 63, 31);

	private static final Color SELECT_COLOR_2 = new Color(127, 0, 63, 0);

	/** The card model. */
	private final Card card;

	/** The selected stated of the card. */
	private boolean selected;

	/** The buffer for caching the drawing of the card in. */
	private BufferedImage bufferedImage;

	/** The width of the card in pixels. */
	private int width;

	/**
	 * Create a new graphical card.
	 * 
	 * @param card the card model being visualised.
	 * @param width the width of the card in pixels.
	 */
	public GraphicalCard(final Card card, int width) {
		this.card = card;
		this.width = width;
		this.bufferedImage = new BufferedImage(width, getHeight(), BufferedImage.TYPE_INT_ARGB);
		bufferedImage.getGraphics().drawImage(getCardImage(width), 0, 0, null);
	}

	private Image getCardImage(int width) {
		return this.card.getCardRenderer().getImage(width, getHeight());
	}

	private Shape s;

	/**
	 * {@inheritdoc}
	 */
	public boolean contains(Point p, int x, int y, double angle) {
		// TODO cached shape
		Point position = getPosition();
		final AffineTransform t = AffineTransform.getRotateInstance(getAngle() + angle, x + position.x, y + position.y);
		return t.createTransformedShape(getShape()).contains(p);
	}

	private Shape getShape() {
		if (s == null) {
			final Point absolutePosition = getAbsolutePosition();
			final int x = absolutePosition.x;
			final int y = absolutePosition.y;
			s = new RoundRectangle2D.Double(x - getWidth() / 2, y - getHeight() / 2, getWidth(), getHeight(), getArcWidth(), getArcHeight());
		}
		return s;
	}

	public int getHeight() {
		return (int) (getWidth() * Math.sqrt(2));
	}

	private int getArcHeight() {
		return getArcWidth();
	}

	private int getArcWidth() {
		return getWidth() / 10;
	}

	public int getWidth() {
		return width;
	}

	/**
	 * {@inheritdoc}
	 */
	public final void paint(Graphics2D g, int x, int y, double angle) {
		final Point position = getPosition();
		int x2 = position.x;
		int y2 = position.y;
		double absAngle = angle + getAngle();
		g.translate(x, y);
		g.rotate(absAngle, x2, y2);
		paintComponent(g);
		g.rotate(-absAngle, x2, y2);
		g.translate(-x, -y);

		// final AffineTransform t = AffineTransform.getRotateInstance(getAngle()
		// + angle, x + x2, y + y2);
		// g.draw(t.createTransformedShape(getShape()));
	}

	/**
	 * {@inheritdoc}
	 */
	public void paintComponent(final Graphics2D g) {
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

		final int cardHeight = getHeight();
		final Point position = getPosition();
		int x = position.x;
		int y = position.y;
		final int left = x - getWidth() / 2;
		final int top = y - cardHeight / 2;

		g.setColor(SHADOW_COLOUR);
		g.fillRoundRect(left - 1, top - 1, getWidth(), cardHeight + 2, getArcWidth(), getArcHeight());
		g.setColor(Color.WHITE);
		g.fillRoundRect(left, top, getWidth(), cardHeight, getArcWidth(), getArcHeight());
		g.drawImage(bufferedImage, left, top, null);
		if (selected) {
			g.setPaint(new GradientPaint(left, top, SELECT_COLOR_1, left + getWidth(), top + cardHeight / 2, SELECT_COLOR_2));
			g.fillRoundRect(left, top, getWidth(), cardHeight, getArcWidth(), getArcHeight());
		}
		g.setColor(Color.black);
	}

	@Override
	public String toString() {
		return card.toString();
	}

	public boolean isSelected() {
		return selected;
	}

	public void setSelected(boolean selected) {
		this.selected = selected;
		getParent().repaint();
	}
}
